package com.xmjs.baselibrary.network.retrofit;

import com.xmjs.baselibrary.BuildConfig;
import com.xmjs.baselibrary.network.Interceptor.ReceivedInterceptor;
import com.xmjs.baselibrary.network.Interceptor.RequestInterceptor;
import com.xmjs.baselibrary.network.body.UploadRequestBody;
import com.xmjs.baselibrary.network.converter.FastJsonConverterFactory;
import com.xmjs.baselibrary.utils.SP;

import java.io.File;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

/**
 * 网络请求工具类
 */
public class RetrofitUtil {
    //时延
    private static final long TIME_OUT = 20 * 1000;

    /**
     * 获取API接口
     *
     * @param tClass API 类
     * @param host   API的域名
     * @param sp     SharePreference 用于保存token、header等
     * @return
     */
    public static <T> T getService(Class<T> tClass, String host, SP sp) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addInterceptor(new ReceivedInterceptor(sp))
                .addInterceptor(new RequestInterceptor(sp))
                .callTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                .connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                .readTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                .writeTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(true);

        //日志输出
        if (BuildConfig.DEBUG) {
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(loggingInterceptor);
        }

        //https证书认证（如果服务端用的是自己生成的证书，那么Android就需要使用下面的代码对证书进行校验，
        //如果服务端使用的是机构颁发的证书就不需要下面的代码，java会默认信任这类证书）
        HttpsUtil httpsUtil = HttpsUtil.getIntance();
        SSLSocketFactory sslSocketFactory = httpsUtil.setCertificates();
        if (sslSocketFactory != null)
            builder.hostnameVerifier((hostname, session) ->
                    hostname.equals(session.getPeerHost()))//主机名和url的校验
                    .sslSocketFactory(sslSocketFactory, x509TrustManager);//证书的校验

        //Retrofit配置
        OkHttpClient okHttpClient = builder.build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(host)//服务器地址
                .client(okHttpClient)//配置okhttp
                .addConverterFactory(FastJsonConverterFactory.create())//添加 fastJson 解析器
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加 RxJava 适配器
                .build();
        return retrofit.create(tClass);
    }

    /**
     * Retrofit 使用 OkHttp 上传文件时对 file 包装
     *
     * @param key  文件对应的key
     * @param path 文件路径
     * @return
     */
    public static MultipartBody.Part getRequestPart(String key, String path, UploadRequestBody.UploadCallback callback) {
        File file = new File(path);
        UploadRequestBody body = new UploadRequestBody(file, callback);
        // RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        MultipartBody.Part part = MultipartBody.Part.createFormData(key, file.getName(), body);
        return part;
    }

    /**
     * 证书认证管理器
     */
    private static X509TrustManager x509TrustManager = new X509TrustManager() {

        /**
         * 检验客户端证书
         * @param chain
         * @param authType
         * @throws CertificateException
         */
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            //不做处理，信任任意证书
        }

        /**
         * 检验服务端证书
         * @param chain
         * @param authType
         * @throws CertificateException
         */
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            //不做处理，信任任意证书的风险：
            //如果用户手机中安装了一个恶意证书，那么就可以通过中间人攻击的方式进行窃听用户通信以及修改request或者response中的数据；
            //在钓鱼Wifi网络中，同样地，攻击者可以通过设置DNS服务器使客户端与指定的服务器进行通信。攻击者在服务器上部署另一个证书，
            //在会话建立阶段，客户端会收到这张证书，如果客户端忽略这个证书上的异常，或者接受这个证书，就会成功建立会话、开始加密通信。
            //但攻击者拥有私钥，因此可以解密得到客户端发来的数据明文。攻击者还可以模拟客户端，与真正的服务器联系，充当中间人做监听。

            //保护办法：
            //1 公钥锁定:将证书公钥写入客户端代码中，https通信时检查服务端传输的证书公钥与代码中的公钥是否一致。（在checkServerTrusted
            // 该方法中进行比较判断）
            //2 证书锁定:即为客户端颁发公钥证书存放在手机客户端中（使用keystore），在https通信时，在客户端代码中固定去取证书信息，不是
            // 从服务端中获取。

            //本工具类采取第2种方式，因此在该回调中未做任何代码处理，但是在apk做安全监测时可能依然会提示这个风险，我们可以忽略
        }

        /**
         * 返回受信任的X509证书数组
         * @return
         */
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    };

}
